from __future__ import division
import math
import random
import pyglet
from pyglet.gl import *
import vector
import game
import map
import thing
import building
import unit


class TacticalController:
	"""Top-level tactical map event handler and dispatcher"""

	VIEW_ANGLE = -35
	VIEW_NORMAL = (0, 1, math.tan(math.radians(270-VIEW_ANGLE)))

	TILES_WIDE = 15 # Number of map tiles accross

	SCROLL_MOUSE_SPEED = 5.0 # per second
	SCROLL_MOUSE_DELAY = .250 # seconds
	SCROLL_KEY_SPEED = 8.0

	def __init__(self, game_map, rect):
		self.rect = rect
		self.scroll_x = 0.0
		self.scroll_y = 0.0
		self.scroll_dx = 0.0
		self.scroll_dy = 0.0
		left, bottom, right, top = rect
		self.TILE_SKEW = math.cos(math.radians(self.VIEW_ANGLE))
		self.TILES_HIGH = self.TILES_WIDE * (top - bottom) // (right - left) / self.TILE_SKEW
		self.TILE_SCALE_X = self.TILES_WIDE / (right - left) 
	 	self.TILE_SCALE_Y = self.TILES_HIGH / (top - bottom)
		self.max_scroll_x = max(game_map.width - self.TILES_WIDE - 1, 0)
		self.max_scroll_y = max(game_map.height - self.TILES_HIGH - 1, 0)
		self.was_press_inside = False
		self.was_mouse_drag = False
		self.map_view = map.TacticalView(game_map)
		pyglet.clock.schedule_interval(self.on_update, 1.0/30)

	def on_key_press(self, symbol, modifiers):
		if not modifiers:
			if symbol == pyglet.window.key.LEFT:
				self.scroll_dx = -self.SCROLL_KEY_SPEED
				return pyglet.event.EVENT_HANDLED
			elif symbol == pyglet.window.key.RIGHT:
				self.scroll_dx = self.SCROLL_KEY_SPEED
				return pyglet.event.EVENT_HANDLED
			elif symbol == pyglet.window.key.UP:
				self.scroll_dy = self.SCROLL_KEY_SPEED
				return pyglet.event.EVENT_HANDLED
			elif symbol == pyglet.window.key.DOWN:
				self.scroll_dy = -self.SCROLL_KEY_SPEED
				return pyglet.event.EVENT_HANDLED
	
	def on_key_release(self, symbol, modifiers):
		if symbol in (pyglet.window.key.LEFT, pyglet.window.key.RIGHT):
			self.scroll_dx = 0
			return pyglet.event.EVENT_HANDLED
		elif symbol in (pyglet.window.key.UP, pyglet.window.key.DOWN):
			self.scroll_dy = 0
			return pyglet.event.EVENT_HANDLED
	
	def in_tactical_rect(self, x, y):
		"""Return true if the window coords x,y are within the tactical rect"""
		left, bottom, right, top = self.rect
		return left <= x <= right and bottom <= y <= top
	
	def to_map_coords(self, x, y):
		"""Return the 2D map coords for the window coords x, y"""
		left, bottom, right, top = self.rect
		map_x = (x - left) * self.TILE_SCALE_X + self.scroll_x
		map_y = (y - bottom) * self.TILE_SCALE_Y + self.scroll_y
		return map_x, map_y
	
	def on_mouse_press(self, x, y, button, modifiers):
		self.was_mouse_drag = False
		if self.in_tactical_rect(x, y):
			self.was_press_inside = True
			map_x, map_y = self.to_map_coords(x, y)
			return game.state.dispatch_event('on_map_press', map_x, map_y, x, y, button, modifiers)
		self.was_press_inside = False
	
	def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
		if self.was_press_inside:
			self.was_mouse_drag = True
			map_x, map_y = self.to_map_coords(x, y)
			return game.state.dispatch_event('on_map_drag', map_x, map_y, x, y, buttons, modifiers)

	def on_mouse_release(self, x, y, button, modifiers):
		if self.was_press_inside:
			map_x, map_y = self.to_map_coords(x, y)
			release_handled = game.state.dispatch_event(
				'on_map_release', map_x, map_y, x, y, button, modifiers)
			if not self.was_mouse_drag and self.in_tactical_rect(x, y):
				click_handled = game.state.dispatch_event(
					'on_map_click', map_x, map_y, x, y, button, modifiers)
				return click_handled or release_handled
			return release_handled
	
	def on_update(self, dt):
		self.scroll_x += self.scroll_dx * dt
		self.scroll_x = min(max(self.scroll_x, 0), self.max_scroll_x)
		self.scroll_y += self.scroll_dy * dt
		self.scroll_y = min(max(self.scroll_y, 0), self.max_scroll_y)
	
	def on_draw(self):
		# ISO elements
		left, bottom, right, top = self.rect
		glViewport(left, bottom, (right - left), (top - bottom))
		glMatrixMode(GL_PROJECTION)
		glLoadIdentity()
		glOrtho(
			0, self.TILES_WIDE,
			0, self.TILES_HIGH * self.TILE_SKEW,
			-100, # near clip
			100, # far clip
			)
		glMatrixMode(GL_MODELVIEW)
		glLoadIdentity()
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

		glRotatef(self.VIEW_ANGLE, 1, 0, 0)
		glTranslatef(-self.scroll_x, -self.scroll_y, 0.0)
		self.map_view.draw(self)
		game.state.dispatch_event('on_draw_tactical_iso')

		# Flat elements
		glDisable(GL_LIGHTING)
		glDisable(GL_DEPTH_TEST)
		glMatrixMode(GL_PROJECTION)
		glPushMatrix()
		glLoadIdentity()
		glOrtho(0, (right - left), 0, (top - bottom), -1, 1)
		glMatrixMode(GL_MODELVIEW)
		glPushMatrix()
		glLoadIdentity()

		game.state.dispatch_event('on_draw_tactical_hud')

		glPopMatrix()
		glMatrixMode(GL_PROJECTION)
		glPopMatrix()
		glMatrixMode(GL_MODELVIEW)
		glEnable(GL_DEPTH_TEST)
		glEnable(GL_LIGHTING)


class SelectionHandler:

	def __init__(self):
		self.reset()

	def reset(self):
		self.mouse_rect = None
		self.map_rect = None

	def on_draw_tactical_hud(self):
		if self.mouse_rect is not None:
			start_x, start_y, current_x, current_y = self.mouse_rect
			glBegin(GL_LINE_LOOP)
			glColor3f(1, 1, 1)
			glVertex3f(min(start_x, current_x), min(start_y, current_y), 1)
			glVertex3f(max(start_x, current_x), min(start_y, current_y), 1)
			glVertex3f(max(start_x, current_x), max(start_y, current_y), 1)
			glVertex3f(min(start_x, current_x), max(start_y, current_y), 1)
			glEnd()

	def on_key_press(self, symbol, modifiers):
		if symbol == pyglet.window.key.ESCAPE and game.local_player.selected:
			game.local_player.selected.empty()
			self.reset()
			return pyglet.event.EVENT_HANDLED
		elif symbol == pyglet.window.key.B:
			# XXX This logic should be moved to a group controller
			handled = None
			for b in building.Building.group:
				if b in game.local_player.selected:
					chosen_unit = random.choice(game.local_player.faction.units)
					game.state.request_unit_create(chosen_unit.__name__, game.local_player.id, chosen_unit.health, b.location, b.rally)
					handled = pyglet.event.EVENT_HANDLED
			return handled

	def on_map_press(self, map_x, map_y, x, y, button, modifiers):
		if button == pyglet.window.mouse.LEFT:
			self.mouse_rect = [x, y, x, y]
			self.map_rect = [map_x, map_y, map_x, map_y]
		elif button == pyglet.window.mouse.RIGHT:
			# XXX This logic should be moved to a group controller
			location = vector.Vec3(map_x, map_y, 0)
			for a in game.local_player.selected:
				if a in unit.Unit.group:
					game.state.request_unit_move(a.id, location)
				elif a in building.Building.group:
					a.rally = location
		return pyglet.event.EVENT_HANDLED

	def on_map_drag(self, map_x, map_y, x, y, buttons, modifiers):
		if buttons & pyglet.window.mouse.LEFT and self.map_rect is not None:
			self.mouse_rect[2] = x
			self.mouse_rect[3] = y
			self.map_rect[2] = map_x
			self.map_rect[3] = map_y
			return pyglet.event.EVENT_HANDLED

	def on_map_release(self, map_x, map_y, x, y, button, modifiers):
		if button == pyglet.window.mouse.LEFT and self.map_rect is not None:
			x0, y0, x1, y1 = self.map_rect
			select_left = min(x0, x1)
			select_right = max(x0, x1)
			select_bottom = min(y0, y1)
			select_top = max(y0, y1)
			game.local_player.selected.empty()
			for a in thing.Thing.group:
				if (select_left <= a.location.x <= select_right 
					and select_bottom <= a.location.y <= select_top
					and set(game.local_player.sprites()).intersection(set(a.owner.sprites()))):
					game.local_player.selected.add(a)
			self.reset()
			return pyglet.event.EVENT_HANDLED


class BuildingHandler:

	def __init__(self):
		self.active = False
	
	def on_key_press(self, symbol, modifiers):
		if not self.active and not game.local_player.selected and symbol == pyglet.window.key.B:
			self.active = True
			return pyglet.event.EVENT_HANDLED
		if self.active and symbol == pyglet.window.key.ESCAPE:
			self.active = False
			return pyglet.event.EVENT_HANDLED

	def on_map_press(self, map_x, map_y, x, y, button, modifiers):
		if self.active and button == pyglet.window.mouse.LEFT:
			# build
			chosen_building = random.choice(game.local_player.faction.buildings)
			build_at = vector.Vec3(map_x, map_y, 0)
			game.state.request_building_create(
				chosen_building.__name__, game.local_player.id, chosen_building.health, build_at)
			self.active = False
			return pyglet.event.EVENT_HANDLED

